```html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>JPA 全面解析 | 技术小馆</title>
    <link href="https://cdn.staticfile.org/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
    <link href="https://cdn.staticfile.org/tailwindcss/2.2.19/tailwind.min.css" rel="stylesheet">
    <link href="https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@400;500;600;700&family=Noto+Sans+SC:wght@300;400;500;700&display=swap" rel="stylesheet">
    <script src="https://cdn.jsdelivr.net/npm/mermaid@latest/dist/mermaid.min.js"></script>
    <style>
        body {
            font-family: 'Noto Sans SC', Tahoma, Arial, Roboto, "Droid Sans", "Helvetica Neue", "Droid Sans Fallback", "Heiti SC", "Hiragino Sans GB", Simsun, sans-serif;
            color: #333;
            line-height: 1.8;
        }
        .hero {
            background: linear-gradient(135deg, #6e8efb 0%, #a777e3 100%);
        }
        .code-block {
            background: #282c34;
            border-radius: 8px;
            position: relative;
        }
        .code-block pre {
            margin: 0;
            padding: 1.5rem;
            overflow-x: auto;
        }
        .code-block code {
            color: #abb2bf;
            font-family: 'Courier New', Courier, monospace;
        }
        .code-block .language {
            position: absolute;
            top: 0;
            right: 0;
            padding: 0.25rem 0.75rem;
            background: rgba(255,255,255,0.1);
            border-bottom-left-radius: 8px;
            color: #fff;
            font-size: 0.8rem;
        }
        .section-card {
            transition: transform 0.3s ease, box-shadow 0.3s ease;
        }
        .section-card:hover {
            transform: translateY(-5px);
            box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
        }
        .highlight {
            background: linear-gradient(120deg, #f6d365 0%, #fda085 100%);
            background-repeat: no-repeat;
            background-size: 100% 40%;
            background-position: 0 88%;
        }
        .dropcap::first-letter {
            float: left;
            font-size: 3.5rem;
            line-height: 1;
            margin: 0.2em 0.1em 0 0;
            color: #6e8efb;
            font-weight: bold;
        }
        .diagram-container {
            background: #f8f9fa;
            border-radius: 8px;
            padding: 1.5rem;
            margin: 2rem 0;
        }
        .footer {
            background: #1a202c;
        }
        .nav-link {
            position: relative;
        }
        .nav-link::after {
            content: '';
            position: absolute;
            bottom: -2px;
            left: 0;
            width: 0;
            height: 2px;
            background: #6e8efb;
            transition: width 0.3s ease;
        }
        .nav-link:hover::after {
            width: 100%;
        }
    </style>
</head>
<body class="bg-gray-50">
    <!-- Hero Section -->
    <section class="hero text-white py-20 px-4 md:px-0">
        <div class="container mx-auto max-w-5xl">
            <div class="flex flex-col items-center text-center">
                <h1 class="text-4xl md:text-5xl font-bold mb-6 leading-tight">
                    <span class="inline-block">JPA 全面解析</span>
                </h1>
                <p class="text-xl md:text-2xl max-w-3xl mb-8 opacity-90">
                    Java 持久化 API 的核心概念、最佳实践与高级特性
                </p>
                <div class="flex space-x-4">
                    <a href="#intro" class="bg-white text-indigo-600 px-6 py-3 rounded-full font-medium hover:bg-gray-100 transition duration-300 shadow-md">
                        <i class="fas fa-book-open mr-2"></i>开始阅读
                    </a>
                    <a href="#examples" class="border-2 border-white text-white px-6 py-3 rounded-full font-medium hover:bg-white hover:bg-opacity-10 transition duration-300">
                        <i class="fas fa-code mr-2"></i>代码示例
                    </a>
                </div>
            </div>
        </div>
    </section>

    <!-- Navigation -->
    <nav class="sticky top-0 bg-white shadow-sm z-10">
        <div class="container mx-auto max-w-5xl">
            <div class="flex justify-center py-4">
                <ul class="flex space-x-8">
                    <li><a href="#intro" class="nav-link text-gray-700 hover:text-indigo-600">简介</a></li>
                    <li><a href="#setup" class="nav-link text-gray-700 hover:text-indigo-600">环境搭建</a></li>
                    <li><a href="#annotations" class="nav-link text-gray-700 hover:text-indigo-600">常用注解</a></li>
                    <li><a href="#repository" class="nav-link text-gray-700 hover:text-indigo-600">Repository</a></li>
                    <li><a href="#jpql" class="nav-link text-gray-700 hover:text-indigo-600">JPQL</a></li>
                    <li><a href="#specification" class="nav-link text-gray-700 hover:text-indigo-600">Specification</a></li>
                </ul>
            </div>
        </div>
    </nav>

    <!-- Main Content -->
    <main class="container mx-auto max-w-5xl py-12 px-4 md:px-0">
        <!-- Introduction Section -->
        <section id="intro" class="mb-20">
            <div class="section-card bg-white rounded-xl p-8 shadow-md">
                <h2 class="text-3xl font-bold mb-6 text-indigo-700 flex items-center">
                    <i class="fas fa-info-circle mr-3"></i>JPA 简介
                </h2>
                <div class="dropcap mb-6">
                    <p><span class="highlight font-bold">JPA(Java Persistence API)</span>是Sun官方提出的Java持久化规范，JPA全程是java持久化API，他的目的就是帮助我们提高开发效率，他的核心是Java持久化查询语句（JPQL）对存储在关系数据库中的实体进行查询、在语法上类似于SQL但是操作的是实体对象而已不是直接对数据库表进行操作。</p>
                </div>
                <p class="mb-6">JPA的好处在于在Repository里，对于那些比较简单的逻辑比如单表查询，直接根据名字就可以实现查询逻辑。对于大部分查询来说，真的很方便。但你用多了Mybatis你刚开始使用JPA的时候会感到莫名其妙。JPA对于开发效率有很大提升JPA想要做的就是尽量让你少写sql甚至不写sql基于这种思想JPA实现了它自己的一套语法，注解规则。</p>
                <p>JPA要用各种注解配合来实现数据实体之间一对多，多对多等的关联关系。正因为如此实体变得不像实体而是掺杂逻辑在里面也增加了实体的复杂度 对于复杂的业务来说很容易造成实体间直接或间接的循环引用。JPA有些注解用上了会影响到数据库层面，比如关键外键的注解如果使用默认设置这个外键就真的会应用到数据库表里，在表上建外键还有其他的一些ORM框架也是如此。</p>
            </div>
        </section>

        <!-- Environment Setup -->
        <section id="setup" class="mb-20">
            <div class="section-card bg-white rounded-xl p-8 shadow-md">
                <h2 class="text-3xl font-bold mb-6 text-indigo-700 flex items-center">
                    <i class="fas fa-cogs mr-3"></i>环境搭建
                </h2>
                
                <div class="mb-8">
                    <h3 class="text-xl font-semibold mb-4 text-gray-800 flex items-center">
                        <i class="fas fa-file-code mr-2 text-indigo-500"></i>1. 导入依赖
                    </h3>
                    <div class="code-block">
                        <div class="language">XML</div>
                        <pre><code>&lt;dependency&gt;
  &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
  &lt;artifactId&gt;spring-boot-starter-data-jpa&lt;/artifactId&gt;
&lt;/dependency&gt;

&lt;dependency&gt;
  &lt;groupId&gt;mysql&lt;/groupId&gt;
  &lt;artifactId&gt;mysql-connector-java&lt;/artifactId&gt;
  &lt;scope&gt;runtime&lt;/scope&gt;
  &lt;version&gt;5.1.48&lt;/version&gt;
&lt;/dependency&gt;</code></pre>
                    </div>
                </div>
                
                <div class="mb-8">
                    <h3 class="text-xl font-semibold mb-4 text-gray-800 flex items-center">
                        <i class="fas fa-sliders-h mr-2 text-indigo-500"></i>2. yml配置
                    </h3>
                    <div class="code-block">
                        <div class="language">Properties</div>
                        <pre><code>#自动生成数据库表（关键）
spring.jpa.hibernate.ddl-auto=update
#mysql数据库连接配置（非常重要）
spring.datasource.url=jdbc:mysql://localhost:3306/2202-mysql?serverTimezone=Asia/Shanghai
#数据库用户名
spring.datasource.username=root
#数据库密码
spring.datasource.password=root
#mysql数据库驱动程序（重要）
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
#jpa配置：在控制台显示Hibernate的sql(可选)
spring.jpa.show-sql = true
#其他配置：关闭Thymeleaf 的缓存</code></pre>
                    </div>
                </div>
                
                <div class="mb-8">
                    <h3 class="text-xl font-semibold mb-4 text-gray-800 flex items-center">
                        <i class="fas fa-cube mr-2 text-indigo-500"></i>3. 创建实体类
                    </h3>
                    <div class="code-block">
                        <div class="language">Java</div>
                        <pre><code>@Data
@Entity(name = "t_user")
public class User implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    private String username;
}</code></pre>
                    </div>
                </div>
                
                <div>
                    <h3 class="text-xl font-semibold mb-4 text-gray-800 flex items-center">
                        <i class="fas fa-code mr-2 text-indigo-500"></i>4. 定义接口
                    </h3>
                    <div class="code-block">
                        <div class="language">Java</div>
                        <pre><code>@Repository
public interface UserRepository extends JpaRepository&lt;User,Integer&gt; {
    
    // 这是自定义方法
    public User getUserByIdAndUsername(Integer id,String username);
}</code></pre>
                    </div>
                </div>
            </div>
        </section>

        <!-- Annotations -->
        <section id="annotations" class="mb-20">
            <div class="section-card bg-white rounded-xl p-8 shadow-md">
                <h2 class="text-3xl font-bold mb-6 text-indigo-700 flex items-center">
                    <i class="fas fa-tags mr-3"></i>JPA常用注解
                </h2>
                <div class="flex justify-center">
                    <img src="https://cdn.nlark.com/yuque/0/2022/png/21449790/1658935560210-a2acfb52-e718-446a-8e46-3f9eab08968b.png" alt="JPA常用注解" class="rounded-lg shadow-md">
                </div>
            </div>
        </section>

        <!-- Repository Section -->
        <section id="repository" class="mb-20">
            <div class="section-card bg-white rounded-xl p-8 shadow-md">
                <h2 class="text-3xl font-bold mb-6 text-indigo-700 flex items-center">
                    <i class="fas fa-database mr-3"></i>Repository接口
                </h2>
                
                <div class="mb-8">
                    <h3 class="text-xl font-semibold mb-4 text-gray-800">基本概念</h3>
                    <ol class="list-decimal pl-6 space-y-2">
                        <li>Repository 是一个空接口. 即是一个标识接口</li>
                        <li><strong>若我们定义的接口(dao层)继承了 Repository或者子类，或者添加注解, 则该接口会被 IOC 容器识别为一个 Repository Bean(Proxy).放入到 IOC 容器中</strong>。在该类中定义的方法必须要满足一定规范
                            <ul class="list-disc pl-6 mt-2">
                                <li>继承或者添加@RepositoryDefinition</li>
                                <li>要被JPA的注解扫到才会添加到Spring容器中</li>
                            </ul>
                        </li>
                        <li>也可以通过@RepositoryDefinition 注解来替代继承 Repository 接口</li>
                    </ol>
                </div>
                
                <div class="mb-8">
                    <h3 class="text-xl font-semibold mb-4 text-gray-800">Repository 的子接口</h3>
                    <div class="diagram-container">
                        <div class="mermaid">
                            classDiagram
                                Repository <|-- CrudRepository
                                CrudRepository <|-- PagingAndSortingRepository
                                PagingAndSortingRepository <|-- JpaRepository
                                JpaRepository : +List~T~ findAll()
                                JpaRepository : +List~T~ findAll(Sort sort)
                                JpaRepository : +List~T~ save(Iterable~S~ entities)
                                JpaRepository : +void deleteInBatch(Iterable~T~ entities)
                                
                                class Repository {
                                    <<Interface>>
                                }
                                class CrudRepository {
                                    <<Interface>>
                                    +save(S entity) S
                                    +findOne(ID id) T
                                    +exists(ID id) boolean
                                    +delete(ID id)
                                }
                                class PagingAndSortingRepository {
                                    <<Interface>>
                                    +findAll(Pageable pageable) Page~T~
                                    +findAll(Sort sort) Iterable~T~
                                }
                                class JpaRepository {
                                    <<Interface>>
                                }
                        </div>
                    </div>
                    <ol class="list-decimal pl-6 space-y-2">
                        <li><strong>Repository</strong>：最顶层的接口，是一个空的接口，目的是为了统一所有Repository的类型，且能让组件扫描的时候自动识别。</li>
                        <li><strong>CrudRepository</strong>：是Repository的子接口，提供CRUD的功能</li>
                        <li><strong>PagingAndSortingRepository</strong>：是CrudRepository的子接口，添加分页和排序的功能</li>
                        <li><strong class="text-red-500">JpaRepository</strong>：是PagingAndSortingRepository的子接口，增加了一些实用的功能，比如：批量操作等。</li>
                        <li><strong>JpaSpecificationExecutor</strong>：用来做负责查询的接口</li>
                    </ol>
                </div>
                
                <div>
                    <h3 class="text-xl font-semibold mb-4 text-gray-800">Repository类中方法定义的规范</h3>
                    <ol class="list-decimal pl-6 space-y-2">
                        <li>不是随便声明的. 而需要符合一定的规范</li>
                        <li>查询方法以 find | read | get 开头(比如 find、findBy、read、readBy、get、getBy)</li>
                        <li>涉及条件查询时，条件的属性用条件关键字连接，条件属性以首字母大写。</li>
                        <li>支持属性的级联查询. 若当前类有符合条件的属性, 则优先使用, 而不使用级联属性. 若需要使用级联属性, 则属性之间使用 _ 进行连接</li>
                    </ol>
                    
                    <div class="mt-6 p-4 bg-blue-50 rounded-lg border-l-4 border-blue-500">
                        <h4 class="font-semibold text-blue-800 mb-2">举例说明：</h4>
                        <p>比如findByUserAddressZip()。框架在解析该方法时，首先剔除 findBy，然后对剩下的属性进行解析，详细规则如下（此处假设该方法针对的域对象为 AccountInfo 类型）：</p>
                        <ol class="list-decimal pl-6 mt-2 space-y-1">
                            <li>先判断userAddressZip （根据 POJO 规范，首字母变为小写，下同）是否为 AccountInfo 的一个属性，如果是，则表示根据该属性进行查询；如果没有该属性，继续第二步；</li>
                            <li>从右往左截取第一个大写字母开头的字符串（此处为Zip），然后检查剩下的字符(userAddress)串是否为 AccountInfo 的一个属性，如果是，则表示根据该属性进行查询；如果没有该属性，则重复第二步，继续从右往左截取；最后假设 user 为 AccountInfo 的一个属性；</li>
                            <li>接着处理剩下部分（AddressZip ），先判断 user 所对应的类型是否有 addressZip 属性，如果有，则表示该方法最终是根据 "AccountInfo.user.addressZip" 的取值进行查询；否则继续按照步骤 2 的规则从右往左截取，最终表示根据 "AccountInfo.user.address.zip" 的值进行查询。</li>
                        </ol>
                        <p class="mt-2">可能会存在一种特殊情况，比如AccountInfo 包含一个 user 的属性，也有一个 userAddress 属性，则优先使用当前类的属性, 而不使用级联属性. 若需要使用级联属性, 则属性之间使用 _ 进行连接。</p>
                    </div>
                    
                    <div class="mt-6 flex justify-center">
                        <img src="https://cdn.nlark.com/yuque/0/2022/png/21449790/1658936729049-190e1bab-dbcc-41c6-8d28-c662d501aabd.png" alt="Repository方法命名规则" class="rounded-lg shadow-md">
                    </div>
                </div>
            </div>
        </section>

        <!-- JPQL Section -->
        <section id="jpql" class="mb-20">
            <div class="section-card bg-white rounded-xl p-8 shadow-md">
                <h2 class="text-3xl font-bold mb-6 text-indigo-700 flex items-center">
                    <i class="fas fa-search mr-3"></i>JPQL
                </h2>
                
                <div class="mb-8">
                    <h3 class="text-xl font-semibold mb-4 text-gray-800">JPQL是什么</h3>
                    <p>JPQL全称Java Persistence Query Language,操作的是类和属性,其实就是跟HQL一样的。</p>
                </div>
                
                <div class="mb-8">
                    <h3 class="text-xl font-semibold mb-4 text-gray-800">支持语法</h3>
                    <p>JPQL提供的查询语法主要分为三类：</p>
                    <ul class="list-disc pl-6 mt-2 space-y-1">
                        <li>查询用的SELECT 语法</li>
                        <li>更新用的UPDATE 语法</li>
                        <li>删除用的DELETE 语法</li>
                    </ul>
                </div>
                
                <div>
                    <h3 class="text-xl font-semibold mb-4 text-gray-800">@Query查询</h3>
                    
                    <div class="mb-6">
                        <h4 class="font-medium text-gray-700 mb-2">支持JPQL操作</h4>
                        <div class="code-block">
                            <div class="language">Java</div>
                            <pre><code>@Query(value="select s1 from Student s1 where s1.id = (select max(s2.id)from Student s2)")
public Student getMaxIdStudent();</code></pre>
                        </div>
                    </div>
                    
                    <div class="mb-6">
                        <h4 class="font-medium text-gray-700 mb-2">支持原生的SQL</h4>
                        <div class="code-block">
                            <div class="language">Java</div>
                            <pre><code>@Query(value="select s1.* from t_student s1 where s1.id = (select max(s2.id)from t_student s2)",nativeQuery=true)
public Student getMaxIdStudent();</code></pre>
                        </div>
                    </div>
                    
                    <div class="mb-6">
                        <h4 class="font-medium text-gray-700 mb-2">传递参数1：按照顺序</h4>
                        <div class="code-block">
                            <div class="language">Java</div>
                            <pre><code>@Query("select s from Student s where  s.id = ?1 and s.name = ?2")
public Student findUser(Integer id,String name);</code></pre>
                        </div>
                    </div>
                    
                    <div class="mb-6">
                        <h4 class="font-medium text-gray-700 mb-2">传递参数2：按照名称</h4>
                        <div class="code-block">
                            <div class="language">Java</div>
                            <pre><code>@Query("select s from Student s where  s.id = :id and s.name = :name")
public Student findUser2(@Param("name")String name,@Param("id")Integer id);</code></pre>
                        </div>
                    </div>
                    
                    <div class="mb-6">
                        <h4 class="font-medium text-gray-700 mb-2">模糊查询</h4>
                        <div class="code-block">
                            <div class="language">Java</div>
                            <pre><code>@Query("select s from Student s where s.name like %:name%")
public Student findUser3(@Param("name")String name);</code></pre>
                        </div>
                    </div>
                    
                    <div>
                        <h4 class="font-medium text-gray-700 mb-2">@Modifying</h4>
                        <div class="code-block">
                            <div class="language">Java</div>
                            <pre><code>@Modifying
@Query("update Student s set s.name = :name where s.id =:id")
public int updateUserUsername(@Param("id")Integer id,@Param("name")String name);</code></pre>
                        </div>
                        <div class="mt-4 p-4 bg-yellow-50 rounded-lg border-l-4 border-yellow-400">
                            <h5 class="font-semibold text-yellow-800 mb-2">注意：</h5>
                            <ol class="list-decimal pl-6 space-y-1">
                                <li>可以通过JPQL完成DELETE和UPDATE操作，但是不能完成INSERT，因为JPQL不支持INSERT。</li>
                                <li>在@Query中编写更新语句必须要用@Modifying注解修饰，要通知SpringDataJPA这是一个更新操作。</li>
                                <li>在做更新操作的时候需要事务</li>
                                <li>默认情况下SpringDateJPA给每个方法都加事务，它们都是只读事务，不能完成更新。</li>
                            </ol>
                        </div>
                    </div>
                </div>
            </div>
        </section>

        <!-- JpaSpecificationExecutor -->
        <section id="specification" class="mb-20">
            <div class="section-card bg-white rounded-xl p-8 shadow-md">
                <h2 class="text-3xl font-bold mb-6 text-indigo-700 flex items-center">
                    <i class="fas fa-filter mr-3"></i>JpaSpecificationExecutor
                </h2>
                <p class="mb-6">不属于Repository体系，实现一组 JPA Criteria 查询相关的方法</p>
                
                <div class="flex justify-center mb-8">
                    <img src="https://cdn.nlark.com/yuque/0/2022/png/21449790/1659004369551-e07bb8a1-b827-498e-8e62-0eab217ae4ec.png" alt="JpaSpecificationExecutor方法" class="rounded-lg shadow-md">
                </div>
                
                <div class="mb-8">
                    <h3 class="text-xl font-semibold mb-4 text-gray-800">Repository</h3>
                    <div class="code-block">
                        <div class="language">Java</div>
                        <pre><code>public interface IUserDao extends JpaRepository&lt;User, Integer&gt;,JpaSpecificationExecutor&lt;User&gt; {
}</code></pre>
                    </div>
                </div>
                
                <div>
                    <h3 class="text-xl font-semibold mb-4 text-gray-800">调用查询方式</h3>
                    <div class="code-block">
                        <div class="language">Java</div>
                        <pre><code>Pageable pageable = new PageRequest(2, 4);
Specification&lt;User&gt; spec = new Specification&lt;User&gt;() {
    /**
     * root: 代表查询的实体类. 
     * query: 查询条件
     * cb: 用于创建 Criteria 相关对象的工厂. 可以从中获取到 Predicate 对象
     * @return: *Predicate 类型, 代表一个查询条件. 
     */
    @Override
    public Predicate toPredicate(Root&lt;User&gt; root, CriteriaQuery&lt;?&gt; query, CriteriaBuilder cb) {
        Path path1 = root.get("id");
        Path path2 = root.get("username");
        Path path3 = root.get("password");
        //Path&lt;Object&gt; path4 = root.get("user").get("id"); // 级联查询
        Predicate predicate = cb.equal(path1, 5);
        //Predicate predicate = cb.and(cb.gt(path2, 2),cb.gt(path3,3)); // 多条件查询
        return predicate ;
    }
};
Page&lt;User&gt; page = userDao.findAll(spec , pageable);</code></pre>
                    </div>
                </div>
            </div>
        </section>
    </main>

    <!-- Footer -->
    <footer class="footer text-white py-8">
        <div class="container mx-auto max-w-5xl px-4">
            <div class="flex flex-col md:flex-row justify-between items-center">
                <div class="mb-4 md:mb-0">
                    <h3 class="text-xl font-bold">技术小馆</h3>
                    <p class="text-gray-400 mt-1">探索技术，分享知识</p>
                </div>
                <div>
                    <a href="http://www.yuque.com/jtostring" class="text-gray-300 hover:text-white transition duration-300">
                        <i class="fas fa-globe mr-2"></i>http://www.yuque.com/jtostring
                    </a>
                </div>
            </div>
        </div>
    </footer>

    <script>
        // Initialize Mermaid
        mermaid.initialize({
            startOnLoad: true,
            theme: 'default',
            flowchart: {
                useMaxWidth: true,
                htmlLabels: true,
                curve: 'basis'
            },
            securityLevel: 'loose'
        });
        
        // Smooth scrolling for anchor links
        document.querySelectorAll('a[href^="#"]').forEach(anchor => {
            anchor.addEventListener('click', function (e) {
                e.preventDefault();
                
                document.querySelector(this.getAttribute('href')).scrollIntoView({
                    behavior: 'smooth'
                });
            });
        });
    </script>
</body>
</html>
```